﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Forms.Integration;
using UtilZ.Dotnet.Ex.AsynWait;
using UtilZ.Dotnet.WindowsDesktopEx.Base;

namespace UtilZ.Dotnet.WindowsDesktopEx.WPF
{
    /// <summary>
    /// WPF异步执行类
    /// </summary>
    /// <typeparam name="T">异步执行参数类型</typeparam>
    /// <typeparam name="TContainer">容器控件类型</typeparam>
    /// <typeparam name="TResult">异步执行返回值类型</typeparam>
    public class WPFPartAsynExcute<T, TContainer, TResult> : WPFAsynExcuteAbs<T, TContainer, TResult> where TContainer : class
    {
        /// <summary>
        /// 构造函数
        /// </summary>
        public WPFPartAsynExcute()
            : base()
        {

        }


        /// <summary>
        /// 异步执行时新new的等待控件
        /// </summary>
        private IPartAsynWait _excuteNewWaitShadeControl = null;

        /// <summary>
        /// 真正的容器控件
        /// </summary>
        private Panel _containerPanelControl;

        /// <summary>
        /// 真正的遮罩层控件
        /// </summary>
        private FrameworkElement _shadeControl;


        /// <summary>
        /// 创建默认遮罩层控件创建工厂对象
        /// </summary>
        /// <returns></returns>
        protected override IShadeControlFactory CreateDefaultShadeControlFactory()
        {
            return new WPFShadeControlFactoryV1();
        }

        #region 执行准备
        /// <summary>
        /// 执行准备
        /// </summary>
        /// <param name="asynWaitPara"></param>
        /// <param name="containerControl"></param>
        protected override void ExcutePre(PartAsynWaitPara<T, TResult> asynWaitPara, TContainer containerControl)
        {
            //设置遮罩层控件            
            asynWaitPara.AsynWait.ShadeBackground = PartAsynExcuteFactoryWPF.ConvertShadeBackground(asynWaitPara.AsynWaitBackground);

            UIElement containerElementControl = containerControl as UIElement;
            FrameworkElement shadeControl = (FrameworkElement)asynWaitPara.AsynWait;

            Panel panel = this.FindRealContainerControl(containerElementControl);
            this._containerPanelControl = panel;

            FrameworkElement realShadeControl = this.GetShadeControl(shadeControl);
            this._shadeControl = realShadeControl;

            if (panel is Grid)
            {
                this.AddShadeControlToGrid((Grid)panel, realShadeControl);
            }
            else if (panel is StackPanel)
            {
                this.AddShadeControlToStackPanel((StackPanel)panel, realShadeControl);
            }
            else if (panel is Canvas)
            {
                this.AddShadeControlToCanvas((Canvas)panel, realShadeControl);
            }
            else if (panel is WrapPanel)
            {
                this.AddShadeControlToWrapPanel((WrapPanel)panel, realShadeControl);
            }
            else
            {
                throw new NotSupportedException($"不支持的容器类型{this._containerControl.GetType().FullName}");
            }

            this.SetMaxZIndex(this._containerPanelControl, shadeControl);
        }

        private void SetMaxZIndex(Panel panel, FrameworkElement shadeControl)
        {
            int maxZIndex = 0, tmpZIndex;
            foreach (UIElement element in panel.Children)
            {
                tmpZIndex = Grid.GetZIndex(element);
                if (tmpZIndex > maxZIndex)
                {
                    maxZIndex = tmpZIndex;
                }
            }

            maxZIndex = maxZIndex + 1;
            Panel.SetZIndex(shadeControl, maxZIndex);
        }

        private void Panel_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            if (sender is StackPanel)
            {
                this.SetStackPanelShadeControl((StackPanel)sender, this._shadeControl);
            }
            else if (sender is Canvas)
            {
                this.SetCanvasShadeControl((Canvas)sender, this._shadeControl);
            }
            else if (sender is WrapPanel)
            {

            }
            else
            {
                throw new NotSupportedException($"不支持的容器类型{sender.GetType().FullName}");
            }
        }






        private void AddShadeControlToWrapPanel(WrapPanel wrapPanel, FrameworkElement shadeControl)
        {
            this.SetWrapPanelShadeControl(wrapPanel, shadeControl);
            wrapPanel.Children.Add(shadeControl);
            wrapPanel.SizeChanged += Panel_SizeChanged;
        }
        private void SetWrapPanelShadeControl(WrapPanel wrapPanel, FrameworkElement shadeControl)
        {
            shadeControl.Width = wrapPanel.ActualWidth;
            shadeControl.Height = wrapPanel.ActualHeight;

            double left = 0d, top = 0d, tmp;

            Point refPoint = new Point();
            //Point point = rectangle.TranslatePoint(new Point(), canvas);
            Point point;
            if (wrapPanel.Orientation == Orientation.Horizontal)
            {

                foreach (FrameworkElement ele in wrapPanel.Children)
                {
                    if (ele == shadeControl)
                    {
                        continue;
                    }

                    point = ele.TranslatePoint(refPoint, wrapPanel);//有可能行,往后再研究
                    //point = System.Windows.Input.Mouse.GetPosition(ele);
                    tmp = point.Y + ele.Height + ele.Margin.Bottom;
                    if (tmp > top)
                    {
                        top = tmp;
                    }
                }
            }
            else
            {
                foreach (FrameworkElement ele in wrapPanel.Children)
                {
                    if (ele == shadeControl)
                    {
                        continue;
                    }

                    point = System.Windows.Input.Mouse.GetPosition(ele);
                    tmp = point.X + ele.Width + ele.Margin.Right;
                    if (tmp > left)
                    {
                        left = tmp;
                    }
                }
            }

            shadeControl.Margin = new Thickness(0d - left, 0d - top, 0d, 0d);
        }



        private void AddShadeControlToCanvas(Canvas canvas, FrameworkElement shadeControl)
        {
            this.SetCanvasShadeControl(canvas, shadeControl);
            canvas.Children.Add(shadeControl);
            Canvas.SetLeft(shadeControl, 0);
            Canvas.SetTop(shadeControl, 0);
            canvas.SizeChanged += Panel_SizeChanged;
        }

        private void SetCanvasShadeControl(Canvas canvas, FrameworkElement shadeControl)
        {
            shadeControl.Width = canvas.ActualWidth;
            shadeControl.Height = canvas.ActualHeight;
        }






        private void AddShadeControlToStackPanel(StackPanel stackPanel, FrameworkElement shadeControl)
        {
            this.SetStackPanelShadeControl(stackPanel, shadeControl);
            stackPanel.Children.Add(shadeControl);
            stackPanel.SizeChanged += Panel_SizeChanged;
        }

        private void SetStackPanelShadeControl(StackPanel stackPanel, FrameworkElement shadeControl)
        {
            shadeControl.Width = stackPanel.ActualWidth;
            shadeControl.Height = stackPanel.ActualHeight;

            double left = 0d, top = 0d;
            if (stackPanel.Orientation == Orientation.Horizontal)
            {
                foreach (FrameworkElement ele in stackPanel.Children)
                {
                    if (ele == shadeControl)
                    {
                        continue;
                    }

                    left += ele.Margin.Left;
                    left += ele.Margin.Right;
                    left += ele.ActualWidth;
                }
            }
            else
            {
                foreach (FrameworkElement ele in stackPanel.Children)
                {
                    if (ele == shadeControl)
                    {
                        continue;
                    }

                    top += ele.Margin.Top;
                    top += ele.Margin.Bottom;
                    top += ele.ActualHeight;
                }
            }

            shadeControl.Margin = new Thickness(0d - left, 0d - top, 0d, 0d);
        }





        private void AddShadeControlToGrid(Grid grid, FrameworkElement shadeControl)
        {
            shadeControl.HorizontalAlignment = HorizontalAlignment.Stretch;
            shadeControl.VerticalAlignment = VerticalAlignment.Stretch;
            grid.Children.Add(shadeControl);

            if (grid.RowDefinitions.Count > 0)
            {
                Grid.SetRow(shadeControl, 0);
                Grid.SetRowSpan(shadeControl, grid.RowDefinitions.Count);
            }

            if (grid.ColumnDefinitions.Count > 0)
            {
                Grid.SetColumn(shadeControl, 0);
                Grid.SetColumnSpan(shadeControl, grid.ColumnDefinitions.Count);
            }
        }






        private FrameworkElement GetShadeControl(FrameworkElement srcShadeControl)
        {
            FrameworkElement realShadeControl;
            if (base.HasWinformControl)
            {
                var elementHost = new ElementHost();
                elementHost.Child = srcShadeControl;

                var windowsFormsHost = new WindowsFormsHost();
                windowsFormsHost.Background = System.Windows.Media.Brushes.Transparent;
                windowsFormsHost.Child = elementHost;

                realShadeControl = windowsFormsHost;
            }
            else
            {
                realShadeControl = srcShadeControl;
            }

            return realShadeControl;
        }

        private Panel FindRealContainerControl(UIElement element)
        {
            if (element is Panel)
            {
                return (Panel)element;
            }

            if (element is ContentControl)
            {
                var contentControl = (ContentControl)element;
                return this.FindRealContainerControl((UIElement)contentControl.Content);
            }
            else if (element is Border)
            {
                var border = (Border)element;
                return this.FindRealContainerControl(border.Child);
            }

            throw new NotSupportedException($"容器元素类型必须为{typeof(Panel).FullName}的子类,类型{element.GetType().FullName}不支持");
        }
        #endregion



        /// <summary>
        /// 执行结束
        /// </summary>
        protected override void ExcuteFinished()
        {
            Panel containerControl = this._containerPanelControl;
            //移除遮罩层控件
            containerControl.Children.Remove(this._shadeControl);

            if (base.HasWinformControl)
            {
                //如果有Winform子控件,则需要调用Dispose,否则内存泄露,快速上涨
                var windowsFormsHost = (WindowsFormsHost)this._shadeControl;
                var elementHost = (ElementHost)windowsFormsHost.Child;
                elementHost.Child = null;
                elementHost.Dispose();

                windowsFormsHost.Child = null;
                windowsFormsHost.Dispose();
            }


            if (containerControl is Grid)
            {
                //无需其它处理
            }
            else if (containerControl is StackPanel ||
                containerControl is Canvas ||
                containerControl is WrapPanel)
            {
                containerControl.SizeChanged -= Panel_SizeChanged;
            }
            else
            {
                throw new NotSupportedException($"不支持的容器类型{containerControl.GetType().FullName}");
            }

            this._shadeControl = null;

            if (this._excuteNewWaitShadeControl != null)
            {
                this._excuteNewWaitShadeControl.Dispose();
                this._excuteNewWaitShadeControl = null;
            }
        }
    }
}
